-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "esp32c3-wifidemo"
VERSION = "1.0.0"

-- 引入必要的库文件(lua编写), 内部库不需要require
sys = require("sys")
require("sysplus")



-- 兼容V1001固件的
if http == nil and http2 then
    http = http2
end
--获取随机颜色
function radom_color()
    local color = "0x"
    for i=1,6 do
        color = color .. string.format("%x",math.random(0,15))
    end
    return color

end

local ws2812_w = 1
local ws2812_h = 1


local mqtt_host = "iot.isuoge.com"
local mqtt_port = 1883
local mqtt_isssl = false
local client_id = "esp32-c3-002" -- mcu.unique_id()
local user_name = "esp32-c3-002"
local password = "password"
local mqttc = nil

-- gpio定义
local netLedPin = 13 --网络灯

local pinData = {
    fishLight = {
        pin = 2,--定义引脚
        actionTime = 0, --触发的时间，用来记录上次启动时间 方便计算运行时长
        cdTime = 30, -- 关闭触发间隔时间(单位:秒)，防止设备长时间运行
        value = 0, -- 引脚电平
        pinType = "gpio" -- 引脚触发类型  gpio 0 1  pwm 0-100
    }
}


-- gpio引脚初始化
gpio.setup(netLedPin, 0)
gpio.setup(pinData.fishLight.pin,0, gpio.PULLDOWN,gpio.BOTH)
-- 网络状态
local netStatus = false

-- 测试led 已作废
function testIO()
    -- while true do
    --     sys.wait(1000)
    --     local rgb = radom_color()
    --     local color = tonumber(rgb,16)
    --     local buff = zbuff.create({ws2812_w,ws2812_h,24},color)
    --     log.info(PROJECT, "test running...")


    --     -- buff:setFrameBuffer(ws2812_w,ws2812_h,24,tonumber(rgb,16))
    --     log.info(PROJECT, color)
    --     -- 在任务函数中使用wait进行1S延时
    --     -- buff:pixel(0,0,tonumber(rgb,16))
    --     sensor.ws2812b(3,buff,0,10,10,0)

    -- end

    -- pwm控制亮度
    -- while true do
    --     for i=0, 100 do
    --         pwm.open(12, 10000, i)
    --         print(i)
    --         sys.wait(100)
    --     end
    --     for i=99,0, -1 do
    --         pwm.open(12, 10000, i)
    --         print(i)
    --         sys.wait(100)
    --     end

    -- end

    --PWM控制步进电机
    stepPin = 6 --step
    dirPin = 10 --方向
    enPin = 7   --启停
    BUTTON_PIN = 5         -- 按键 GPIO引脚  BOOT/GPIO0

    pwm.open(stepPin, 300, 50)
    gpio.setup(dirPin, 1)
    gpio.setup(BUTTON_PIN, nil, gpio.PULLUP) --按钮 输入模式
    -- gpio.debounce(BUTTON_PIN, 200, 0)--去抖动 点动开关模式可以开启

    -- local count = 0
    -- while true do
    --     count = count+1
    --     if count%2 == 1 then
    --         gpio.setup(enPin, 1)--启动
    --     else
    --         gpio.setup(enPin, 0)--停止
    --     end
    --     print(count)
    --     sys.wait(1000)
    -- end


    -- gpio控制
    -- local gpioPin = 7
    -- gpio.setup(gpioPin, 0)
    -- gpio.set(gpioPin,1)
    -- local count = 0
    -- while true do
    --     count = count+1
    --     if count%2 == 1 then
    --         gpio.set(gpioPin,1)
    --     else
    --         gpio.set(gpioPin,0)
    --     end
    --     print(count)
    --     sys.wait(1000)
    -- end

end


-- 网络灯闪烁
function netLedShine()
    local isLight = false
    while true do
        if isLight then
            gpio.set(netLedPin,0)
        else
            gpio.set(netLedPin,1)
        end
        isLight = not isLight
        if netStatus then
            sys.wait(3000)
        else
            sys.wait(500)
        end

    end
end

--IO操作
function ioHandle(pinName,value)
    for key, item in pairs(pinData) do
        if key == pinName then
            gpio.set(pinData[pinName].pin,value)
            pinData[pinName].value = value
            print('pinName',pinName,pinData[pinName].value)
        end
    end
end


-- sys.taskInit(function()
--     sys.wait(1000)

--     wlan.init()
--     wlan.connect("cats", "18725741170")
--     log.info("wlan", "wait for IP_READY")
--     sys.waitUntil("IP_READY", 30000)
--     if wlan.ready() then
--         log.info("wlan", "ready !!")
--         sys.wait(100)
--         local url = "http://ip.nutz.cn/json"
--         -- local url = "http://nutzam.com/1.txt"
--         local code, headers, body = http.request("GET", url).wait()
--         log.info("http", code, json.encode(headers or {}), body and #body or 0)

--         -- 网络状态
--         netStatus = true
--         sys.subscribe("IP_READY", function(adapter)
--             netStatus = true
--             log.info("网络连接", "IP_READY")
--         end)
--         sys.subscribe("IP_LOSE", function(adapter)
--             netStatus = false
--             log.info("网络丢失", "IP_LOSE")
--         end)

--         --mqtt连接
--         mqttc = mqtt.create(nil,mqtt_host, mqtt_port, mqtt_isssl, ca_file)

--         mqttc:auth(client_id,user_name,password) -- client_id必填,其余选填
--         mqttc:keepalive(30) -- 默认值240s
--         mqttc:autoreconn(true, 3000) -- 自动重连机制

--         mqttc:on(function(mqtt_client, event, data, payload)
--             -- 用户自定义代码
--             log.info("mqtt", "event", event, mqtt_client, data, payload)
--             if event == "conack" then
--                 sys.publish("mqtt_conack")
--                 mqtt_client:subscribe("/iot/smartgarden/"..user_name.."/io_controller")
--             elseif event == "recv" then
--                 log.info("mqtt", "downlink", "topic", data, "payload", payload,os.time(),os.date)
--                 local t = rtc.get()
--                 log.info("rtc", json.encode(t))
--                 -- gpio.set(22, 1)--引脚高电平
--                 local payloadData =  json.decode(payload)
--                 if payloadData.type == "ioHandle" then
--                     --IO操作
--                     ioHandle(payloadData.data.name,payloadData.data.value)
--                     if  payloadData.data.value>0 then--记录开始时间
--                         pinData[payloadData.data.name].actionTime = os.time()
--                     end
--                 end
--             elseif event == "sent" then
--                 log.info("mqtt", "sent", "pkgid", data)
--             -- elseif event == "disconnect" then
--                 -- 非自动重连时,按需重启mqttc
--                 -- mqtt_client:connect()
--             end
--         end)

--         mqttc:connect()
--         sys.waitUntil("mqtt_conack")
--         while true do
--             -- mqttc自动处理重连
--             local ret, topic, data, qos = sys.waitUntil("mqtt_pub", 30000)
--             if ret then
--                 if topic == "close" then break end
--                 mqttc:publish(topic, data, qos)
--             end
--         end
--         mqttc:close()
--         mqttc = nil






--     else
--         print("wlan NOT ready!!!!")
--     end

-- end)

--网络状态灯，单独开协程处理是避免sys.wait()对其他逻辑的阻塞
-- sys.taskInit(function ()
--     netLedShine()
-- end)



--添加硬狗防止程序卡死
if wdt then
    wdt.init(9000)--初始化watchdog设置为9s
    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
end

--mqtt事件处理
-- sys.taskInit(function()
-- 	local topic = "/iot/smartgarden/"..user_name.."/io_status"
-- 	-- local data = "123"
-- 	-- local qos = 1
--     while true do
--         -- 5秒发送一次IO状态数据
--         sys.wait(5000)
--         if mqttc and mqttc:ready() then
--             --读取引脚数据
--             local data = {}
--             for key, item in pairs(pinData) do
--                 data[key] = pinData[key].value
--             end
--             --发布数据
--             jsondata = json.encode({type="ioStatus",data=data})
--             local pkgid = mqttc:publish(topic, jsondata, 1)

--         end
--     end
-- end)

local commandMsg = {
    "verandaLight",
    "verandaLight",
    "topLight",
    {
        "verandaLight",
        "topLight",
    },
    "verandaLight",
    "verandaLight",
    "topLight",
    {
        "verandaLight",
        "topLight",
    },
}


--根据串口反馈接收到的语音命令ID发送MQtt消息控制IO
function setLightByMqtt(commandid)

    commandid = tonumber(commandid)
    ioName = commandMsg[commandid]
    if type(ioName)=='table' then
        for key, item in pairs(ioName) do
            -- log.info("arrData: ", key, item)
            sendByName(item,commandid)
        end

    else
        sendByName(ioName,commandid)
    end

end

function sendByName(ioName,id)
    local topic = "/iot/smartgarden/io_controller"
    if mqttc and mqttc:ready() then
        local data = {name = ioName,}
        if id>4 then
            data["value"] = 0
        else
            data["value"] = 1
        end
        jsondata = json.encode({type="ioHandle",data=data})
        -- log.info("send Data: ", jsondata)
        local pkgid = mqttc:publish(topic, jsondata, 1)
    end
end

--处理IO运行超时
-- sys.taskInit(function()
--     while true do
--         sys.wait(1000)
--         --工作超时保护，防止设备运行超时（比如浇水水泵）  配置cdTime大于0时生效 为0则可以不间断运行（比如过滤泵）
--         for key, item in pairs(pinData) do
--             if pinData[key].cdTime >0 and pinData[key].value>0 and  os.time() - pinData[key].actionTime > pinData[key].cdTime then
--                 ioHandle(key,0)
--                 print('触发超时保护',key,pinData[key].value)
--             end
--         end

--     end
-- end)


-- --初始化串口信息
-- uart.setup(1, 115200, 8, 1, uart.NONE)
-- --处理串口信息
-- sys.taskInit(function ()
--     local str = "page0.t5.txt=\"淘晶驰\""
--     local specialChars = string.char(0xff, 0xff, 0xff)
--     uart.write(1, str .. specialChars)
--     uart.on(1,"receive",function (id,len)
--         local data = uart.read(id, len)
--         log.info("uart--receive: ", id, len, data)
--         -- setLightByMqtt(data)
--     end)
-- end)

--IO测试
sys.taskInit(function ()
    testIO()
end)

-- 默认配置
initConfig = {
    washCdTime = 10, --延时时间 10-60 秒
    runMode= 1, --运行模式 1自动2定时
    drumSpeed = 50,--转桶速度 1-100 %
    timerGap = 30, --定时间隔 /分钟
}

runModeTxt = {[1]="自动",[2]="定时"}
-- 冲洗记录
washTime = 0 --冲洗时长
washCount = 0 --冲洗次数

-- 数据库初始化
if fskv.init() then
    log.info("fdb", "kv数据库初始化成功")
end
washConfig = fskv.get('wash-config')
if washConfig then
    print("历史数据获取成功")
    print("==当前运行配置==")
    print(" runMode = ",washConfig.runMode)
    print(" washCdTime = ",washConfig.washCdTime)
    print(" drumSpeed = ",washConfig.drumSpeed)
    print(" timerGap = ",washConfig.timerGap)
else
    print('初始化配置')
    -- 初次初始化直接set存表  下次修改数据使用fskv.sett(key, skey, value)
    fskv.set('wash-config',initConfig)
end



--反冲洗任务
sys.taskInit(function ()
    while true do

        if washTime>0 then
            print("反冲洗开启")
            gpio.setup(enPin, 0)
            washTime = washTime-1
        else
            print("反冲洗关闭")
            gpio.setup(enPin, 1)
            washTime = 0
        end
        sys.wait(1000)
    end
end)




-- 水位检测任务 （忽略模式 任何模式下水位异常都开启反冲洗）
sys.taskInit(function()
    -- local press_start_time = 0
    -- local press_start_status = false
    while true do
        if gpio.get(BUTTON_PIN) == 0 then -- 按下
            print("水位高度异常")
            washTime = washConfig.washCdTime
        end
        sys.wait(50)
    end
end)


-- 定时任务函数
function timer_func(state)
    log.info("==定时时间到==",washConfig.timerGap, state)
    washTime = washConfig.washCdTime
end


-- 定时器
sys.taskInit(function()
    timerId = nil
    if  washConfig.runMode == 2 then
        timerId = sys.timerLoopStart(timer_func,washConfig.timerGap*60*1000)
        log.info("定时器启动", timerId)
    end
    -- 订阅定时器变更操作 （模式变更 or 时长变更）
    sys.subscribe("TIMER_CHANGE", function(state)
        log.info("sys", "定时器时间变更", state)
        if timerId then
            sys.timerStop(timerId)
            log.info("定时器停止", timerId)
        end
        -- 启动新的定时器
        timerId = sys.timerLoopStart(timer_func,washConfig.timerGap*60*1000)
        log.info("定时器启动", timerId)
    end)

end)


-- 定义startsWith函数
local function starts_with(str, prefix)
    return #str >= #prefix and str:sub(1, #prefix) == prefix
end
-- 字符串转数组
function split(str, delimiter)
    local result = {}
    for match in string.gmatch(str, "([^"..delimiter.."]+)") do
        table.insert(result, match)
    end
    return result
end

-- 处理屏幕数据 并存入数据库
local function handelScreenData(data)
    local arrData = split(data, "-")
    local valValue = tonumber(arrData[2])
    washConfig[arrData[1]] = valValue
    print("==存储数据==",arrData[1],valValue)
    fskv.sett('wash-config',arrData[1],valValue)
    -- 模式更改或定时间隔变更
    if arrData[1]=='timerGap' or arrData[1]=='runMode' then
        sys.publish("TIMER_CHANGE", 'timergap or runmode change')
    end
end

-- 同步屏幕数据
function syncScreenData()
    -- 发送初始状态数据
    data1 = "page0.t5.txt=\""..runModeTxt[washConfig.runMode].."\"" .. string.char(0xff,0xff,0xff)
    uart.write(1, data1)
    data2 = "page0.t6.txt=\""..washConfig.washCdTime.."\"" .. string.char(0xff,0xff,0xff)
    uart.write(1, data2)
    data3 = "page0.t7.txt=\""..washConfig.drumSpeed.."\"" .. string.char(0xff,0xff,0xff)
    uart.write(1, data3)
    data4 = "page0.t9.txt=\""..washConfig.timerGap.."\"" .. string.char(0xff,0xff,0xff)
    uart.write(1, data4)
end

-- 串口初始化及监听
sys.taskInit(function()
    sys.wait(3000)--延时3s等待屏幕启动稳定
    -- 初始化串口
    uart.setup(1, 9600, 8, 1, uart.NONE)
    syncScreenData()

    -- 监听串口接收事件
    uart.on(1, "receive", function(id, len)
        local newData = uart.read(id, len)
        -- print("串口接收：", newData)
        print("收到新数据=>", newData)
        -- 配置变化
        if starts_with(newData, "runMode") or starts_with(newData, "washCdTime") or starts_with(newData, "drumSpeed") or starts_with(newData, "timerGap") then
            -- 处理配置数据逻辑
            handelScreenData(newData)
        end
        -- 同步数据请求
        if starts_with(newData, "syncData") then
            -- 处理同步数据逻辑
            syncScreenData()
        end
    end)
end)


-- 时钟同步任务
sys.taskInit(function()
    while true do
        sys.wait(1000)
        -- -- 获取当前时间
        -- local currentTime = rtcm.getTime()
        -- -- 将时间转换为字符串
        -- local timeString = string.format("%02d:%02d:%02d", currentTime.hour, currentTime.min, currentTime.sec)
        -- -- 发送时间到屏幕
        -- sendScreenData("time", timeString)
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
